﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Diplomova_prace
{

    class Nelinear_Solutions_alg
    {
        Polynoms p = new Polynoms();
        Expression v = new Expression();
        Derivation_alg d = new Derivation_alg();
        Nelin_ret ret = new Nelin_ret();
        public delegate void BolzanHandler();
        public delegate void NullDenominatorHandler();
        public delegate void NullValueHandler();
        public delegate void PostHandler();
        public event BolzanHandler error;
        public event NullDenominatorHandler nullDenomi;
        public event NullValueHandler nullValue;
        public event PostHandler post;
        bool incorrect = false;
        public Nelinear_Solutions_alg()
        {
            v.incorrectInput += v_incorrectInput;
        }

        void v_incorrectInput()
        {
            if (!incorrect)
                post();
            incorrect = true;
        }
        public Nelin_ret Newton(string function, double start,int iteration,double accuracy,string derivate = "")
        {
            incorrect = false;
            double xNew = double.MinValue;
            double xPrev = start;
            string parser = v.RPN(function);
            List<double> X = new List<double>();
            List<double> Y = new List<double>();
            List<double> DerY = new List<double>();
            double fx;
            double derFx;
            for (int i = 0; i < iteration; i++)
            {
                  fx = double.NaN;
                  derFx = double.NaN;
                      fx = v.ComputeRPN(parser, xPrev);
                      X.Add(xPrev);
                      Y.Add(fx);
                      if (derivate != "")
                          derFx = v.ComputeRPN(v.RPN((derivate)), xPrev);
                      else
                          derFx = d.QudraticDerivate(function, xPrev).Result;
                      DerY.Add(derFx);
                      xNew = xPrev - (fx / derFx);
                      if (Math.Abs(xNew - xPrev) < accuracy || derFx == 0 || fx == 0)
                      {
                          X.Add(xNew);
                          Y.Add(v.ComputeRPN(parser,xNew));
                          if (derFx == 0 && fx != 0)
                              nullDenomi();
                          ret.xProperty = X;
                          ret.fxProperty = Y;
                          ret.derfxProperty = DerY;
                          return ret;
                      }
                      if (double.IsNaN(fx) || double.IsNaN(xPrev))
                      {
                          nullValue();
                          ret.xProperty = X;
                          ret.fxProperty = Y;
                          ret.derfxProperty = DerY;
                          return ret;
                      }
                      xPrev = xNew;
            }
            ret.xProperty = X;
            ret.fxProperty = Y;
            ret.derfxProperty = DerY;
            return ret;
        }
        public Nelin_ret BisectionMethod(string function, double a, double b, int iteration, double accuracy) //Puleni intervalu
        {
            incorrect = false;
            string parser = v.RPN(function);
            int step = 1;
            List<double> X = new List<double>();
            List<double> Y = new List<double>();
            double fa = v.ComputeRPN(parser, a);
            double fb = v.ComputeRPN(parser, b);
            double sOld = double.MaxValue;
            X.Add(a);
            X.Add(b);
            Y.Add(fa);
            Y.Add(fb);
            while (fa * fb < 0)
            {
                if (step > iteration)
                {
                    ret.xProperty = X;
                    ret.fxProperty = Y;
                    return ret;
                }
                
                double s = (a + b) / 2;
                double fs = v.ComputeRPN(parser, s);
                X.Add(s);
                Y.Add(fs);
                if (fs == 0)
                {
                    ret.xProperty = X;
                    ret.fxProperty = Y;
                    return ret;
                }
                if (Math.Abs(s - sOld) < accuracy)
                {
                    ret.xProperty = X;
                    ret.fxProperty = Y;
                    return ret;
                }
                else
                    sOld = s;
                if (fa == 0)
                {
                    ret.xProperty = X;
                    ret.fxProperty = Y;
                    return ret; 
                }
                else if (fb == 0)
                {
                    ret.xProperty = X;
                    ret.fxProperty = Y;
                    return ret;
                }
                if (fa * fs < 0)
                {
                    b = s;
                }
                else if (fs * fb < 0)
                {
                    a = s;
                }
               
                fa = v.ComputeRPN(parser, a);
                fb = v.ComputeRPN(parser, b);
                if (double.IsNaN(fa) || double.IsNaN(fb) || double.IsNaN(a) || double.IsNaN(b) || double.IsNaN(s))
                {
                    nullValue();
                    ret.xProperty = X;
                    ret.fxProperty = Y;
                    return ret;
                }
                step++;
            }
            if (fa == 0 || fb == 0)
            {
                if (X.Count == 0)
                {
                    if (fa == 0)
                    {
                        X.Add(a);
                        Y.Add(fa);
                    }
                    else
                    {
                        X.Add(b);
                        Y.Add(fb);
                    }
                    ret.xProperty = X;
                    ret.fxProperty = Y;
                }
                else
                {
                    ret.xProperty = X;
                    ret.fxProperty = Y;
                }
                return ret;
            }
            else
            {
                error();
            }

             //   zprava = "Funkční hodnoty na krajích intervalu nemají opačné znaménko";
            ret.xProperty = X;
            ret.fxProperty = Y;
            return ret;

        }
        public Nelin_ret RegulaFalsi(string function, double a, double b, int iteration, double accuracy)
        {
            incorrect = false;
            string parser = v.RPN(function);
            int step = 1;
            List<double> X = new List<double>();
            List<double> Y = new List<double>();
            double fa = v.ComputeRPN(parser, a);
            double fb = v.ComputeRPN(parser, b);
            double sOld = double.MaxValue;
            X.Add(a);
            X.Add(b);
            Y.Add(fa);
            Y.Add(fb);
            while (fa * fb < 0)
            {
                if (step > iteration)
                {
                    ret.xProperty = X;
                    ret.fxProperty = Y;
                    return ret;
                }
               
                double s = (a*fb-b*fa)/ (fb - fa);
                double fs = v.ComputeRPN(parser, s);
                X.Add(s);
                Y.Add(fs);
                if (fs == 0)
                {
                    ret.xProperty = X;
                    ret.fxProperty = Y;
                    return ret;
                }
                if (fa * fs < 0)
                {
                    b = s;
                }
                else if (fs * fb < 0)
                {
                    a = s;
                }
                if (fa == 0)
                {
                    ret.xProperty = X;
                    ret.fxProperty = Y;
                    return ret;
                }
                else if (fb == 0)
                {

                    ret.xProperty = X;
                    ret.fxProperty = Y;
                    return ret;
                }
                if (Math.Abs(s - sOld) < accuracy)
                {
                    ret.xProperty = X;
                    ret.fxProperty = Y;
                    return ret;
                }
                else
                    sOld = s;

                fa = v.ComputeRPN(parser, a);
                fb = v.ComputeRPN(parser, b);
                if (double.IsNaN(fa) || double.IsNaN(fb) || double.IsNaN(a) || double.IsNaN(b) || double.IsNaN(s))
                {
                    nullValue();
                    ret.xProperty = X;
                    ret.fxProperty = Y;
                    return ret;
                }
                step++;
            }
            if (fa == 0 || fb == 0)
            {
                if (X.Count == 0)
                {
                    if (fa == 0)
                    {
                        X.Add(a);
                        Y.Add(fa);
                    }
                    else
                    {
                        X.Add(b);
                        Y.Add(fb);
                    }
                    ret.xProperty = X;
                    ret.fxProperty = Y;
                }
                else
                {
                    ret.xProperty = X;
                    ret.fxProperty = Y;
                }
                return ret;
            }
            else
            {
                error();
            }
                ret.xProperty = X;
                ret.fxProperty = Y;
                return ret;
        }
        public Nelin_ret SecantMethod(string function, double a, double b, int iteration, double accuracy)
        {
            //a,b interval
            incorrect = false;
            string parser = v.RPN(function);
            double fa = v.ComputeRPN(parser, a);
            double fb = v.ComputeRPN(parser, b);
            List<double> X = new List<double>();
            List<double> Y = new List<double>();
            double s = double.MaxValue;
            double sOld = double.MinValue;
            double fs;
            int step = 1;
            X.Add(a);
            X.Add(b);
            Y.Add(fa);
            Y.Add(fb);
            if (fa != 0 && fb != 0)
            {
                while (Math.Abs(s - sOld) > accuracy)
                {
                    if (step > iteration)
                    {
                        ret.xProperty = X;
                        ret.fxProperty = Y;
                        return ret;
                    }
                    step++;
                    sOld = s;
                    s = b - ((b - a) / (fb - fa)) * fb;
                    fs = v.ComputeRPN(parser, s);
                    X.Add(s);
                    Y.Add(fs);
                    a = b;
                    fa = v.ComputeRPN(parser, b);
                    b = s;
                    fb = v.ComputeRPN(parser, s);
                    if (double.IsNaN(fa) || double.IsNaN(fb) || double.IsNaN(a) || double.IsNaN(b) || double.IsNaN(s))
                    {
                        nullValue();
                        ret.xProperty = X;
                        ret.fxProperty = Y;
                        return ret;
                    }

                }
            }
            ret.xProperty = X;
            ret.fxProperty = Y;
            return ret;
        }
        public Nelin_ret SteffensenovaMetoda(string function, double start, int iteration, double accuracy)
        {
            incorrect = false;
            string parser = v.RPN(function);
            double xNew = double.MinValue;
            double xPrev = start;
            double dn;
            List<double> X = new List<double>();
            List<double> Y = new List<double>();
            List<double> D = new List<double>();
            List<List<double>> XY = new List<List<double>>();
            double fx;
            double derFx;
            for (int i = 0; i < iteration; i++)
            {
                fx = double.NaN;            
                derFx = double.NaN;
                fx = v.ComputeRPN(parser, xPrev);
                X.Add(xPrev);
                Y.Add(fx);
                
                dn = v.ComputeRPN(parser, fx + xPrev);
                derFx = (dn - fx);
                D.Add(dn);
                xNew = xPrev - (Math.Pow(fx,2) / derFx);
                if (Math.Abs(xNew - xPrev) < accuracy || fx == 0 || derFx == 0)
                {
                    X.Add(xNew);
                    Y.Add(v.ComputeRPN(parser, xNew));
                    ret.xProperty = X;
                    ret.fxProperty = Y;
                    ret.derfxProperty = D;
                    if (derFx == 0 && fx != 0)
                        nullDenomi();
                    return ret;
                }
                if (double.IsNaN(fx) || double.IsNaN(derFx) || double.IsNaN(dn) || double.IsNaN(xPrev))
                {
                    nullValue();
                    ret.xProperty = X;
                    ret.fxProperty = Y;
                    ret.derfxProperty = D;
                    return ret;
                }
                xPrev = xNew;
            }
            ret.xProperty = X;
            ret.fxProperty = Y;
            ret.derfxProperty = D;
            return ret;
        }
        public Nelin_ret HalleyMethod(string function, double start, int iteration, double accuracy,string derivate = "",string derivate2="")
        {
            incorrect = false;
            double xNew = double.MinValue;
            double xPrev = start;
            string parser = v.RPN(function);
            double fx = double.NaN;
            double derFx = double.NaN;
            double secondDerFx = double.NaN;
            double nominator = double.NaN;
            double denominator = double.NaN;
            List<double> X = new List<double>();
            List<double> Y = new List<double>();
            List<double> D = new List<double>();
            List<double> DD = new List<double>();
            List<List<double>> XY = new List<List<double>>();
            for (int i = 0; i < iteration; i++)
            {
                    fx = v.ComputeRPN(parser, xPrev);
                    X.Add(xPrev);
                    Y.Add(fx);
                    if (derivate != "")
                        derFx = v.ComputeRPN(v.RPN((derivate)), xPrev);
                    else
                        derFx = d.QudraticDerivate(function, xPrev).Result;
                    if (derivate2 != "")
                        secondDerFx = v.ComputeRPN(v.RPN((derivate2)), xPrev);
                    else
                        secondDerFx = d.QudraticSecondDerivate(function, xPrev).Result;
                    D.Add(derFx);
                    DD.Add(secondDerFx);
                    nominator = 2 * fx * derFx;
                    denominator = 2 * Math.Pow(derFx, 2) - fx * secondDerFx;
                    xNew = xPrev - (nominator / denominator);
                    if (Math.Abs(xNew - xPrev) < accuracy || fx == 0 || denominator == 0)
                    {
                        X.Add(xNew);
                        Y.Add(v.ComputeRPN(parser, xNew));
                        ret.xProperty = X;
                        ret.fxProperty = Y;
                        ret.derfxProperty = D;
                        ret.secondDerFxProperty = DD;
                        if (denominator == 0 && fx != 0)
                            nullDenomi();
                        return ret;
                    }
                    if (double.IsNaN(fx) || double.IsNaN(derFx) || double.IsNaN(secondDerFx) || double.IsNaN(xPrev) || double.IsNaN(nominator) || double.IsNaN(denominator))
                    {
                        nullValue();
                        ret.xProperty = X;
                        ret.fxProperty = Y;
                        ret.derfxProperty = D;
                        ret.secondDerFxProperty = DD;
                        return ret;
                    }
             
                    xPrev = xNew;
            }
            ret.xProperty = X;
            ret.fxProperty = Y;
            ret.derfxProperty = D;
            ret.secondDerFxProperty = DD;
            return ret;
        }
    }
    class Nelin_ret
    {
        List<double> x;
        List<double> fx;
        List<double> derfx;
        List<double> secondDerfx;
        public List<double> xProperty
        {
            get { return x; }
            set { x = value; }
        }
        public List<double> fxProperty
        {
            get { return fx; }
            set { fx = value; }
        }
        public List<double> derfxProperty
        {
            get { return derfx; }
            set { derfx = value; }
        }
        public List<double> secondDerFxProperty
        {
            get { return secondDerfx; }
            set { secondDerfx = value; }
        }

    }
}
